﻿using System;

namespace RLProject1.Model.AMEEWorld
{

    public sealed class Vergeltungssunktion
    {
        /// <summary>
        /// (1.5*(1-e^(u/0.5)))+(10*(1-e^(-s/0.4))-10)
        /// </summary>
        public static readonly Vergeltungssunktion StandardVergeltung = new Vergeltungssunktion
        {
            MaxWeitenBestrafung = -10.0,
            WeitenStauchung = 0.4,
            UnebenheitsKonstante = 1.5,
            UnebenheitsStauchung = 0.5
        };

        /// <summary>
        /// Höchststrafe für Stehenbleiben (negativer Wert erwartet).
        /// </summary>
        public double MaxWeitenBestrafung { get; set; }

        /// <summary>
        /// Bestrafung nähert sich umgekehrt exponentiell der 0 an. Dieser Wert gibt an,
        /// wann 36,8% der MaxWeitenBestrafung erreicht werden, womit man steuern kann,
        /// wie schnell sich die Weitenbestrafung der 0 nähert.
        /// </summary>
        public double WeitenStauchung { get; set; }

        /// <summary>
        /// Positiver Wert erwartet.<para>
        /// Die Bestrafung für Unebenheit fällt von 0 für absolut glatt exponentiell ins negativ Unendliche.
        /// Die UnebenheitsKonstante 'C' gibt zusammen mit der UnebeheitsStauchung 'Us' an, wie schnell die Bestrafung wächst.
        /// Bei einer Unebenheit von 'Us' sind 172% von der UnebenheitsKonstante erreicht.</para><para>
        /// ??? Naja !!! Sieht einfach gut aus wenn bspw. C=1,5 und Us=0,5 gewählt werden</para>
        /// </summary>
        public double UnebenheitsKonstante { get; set; }

        /// <summary>
        /// Positiver Wert von 0.0 bis 1.0 erwartet.<para>
        /// Die Bestrafung für Unebenheit fällt von 0 für absolut glatt exponentiell ins negativ Unendliche.
        /// Die UnebenheitsKonstante 'C' gibt zusammen mit der UnebeheitsStauchung 'Us' an, wie schnell die Bestrafung wächst.
        /// Bei einer Unebenheit von 'Us' sind 172% von der UnebenheitsKonstante erreicht.</para><para>
        /// ??? Naja !!! Sieht einfach gut aus wenn bspw. C=1,5 und Us=0,5 gewählt werden</para>
        /// </summary>
        public double UnebenheitsStauchung { get; set; }

        /// <summary>
        /// Die Vergeltungsfunktion R² -> R.<para>
        /// Wird sie nicht gesetzt oder verändert, wird eine interne Standardfunktion genutzt, die eine
        /// Linearkombination natürlich exponentieller Funktionen der Unebenheit und Weite umsetzt.</para>
        /// </summary>
        public Func<double, double, double> Eval { get; set; }

        /// <summary>
        /// Initialisiert eine neue Instanz der <see cref="Vergeltungssunktion"/> mit einer Standardumsetzung der Eval Funktion. Die Parameter sind alle 0,0.
        /// </summary>
        /// <seealso cref="Vergeltungssunktion.StandardVergeltung"/>
        public Vergeltungssunktion()
        {
            this.Eval = this.EvalStandard;
        }

        /// <summary>
        /// Berechnet die Bestrafung zu gegebenem Unebenheitsmaß (0 bis 1) und gegebener Schrittweite.<para>
        /// Für glatte Böden und große Schritte geht die Bestrafung gegen 0. Sie steigt exponentiell mit der Unebenheit und
        /// für kurze Schritte.</para><para>
        /// </para>
        /// </summary>
        /// <param name="unebenheit">The unebenheit.</param>
        /// <param name="weite">The weite.</param>
        /// <returns></returns>
        internal double EvalStandard(double unebenheit, double weite)
        {
            double retVal = MaxWeitenBestrafung;
            double temp = MaxWeitenBestrafung * (1 - Math.Pow(Math.E, -weite / this.WeitenStauchung));
            retVal -= temp;
            temp = this.UnebenheitsKonstante * (1 - Math.Pow(Math.E, unebenheit / this.UnebenheitsStauchung));
            retVal += temp;
            return retVal;
        }


    }

}
